package pl.edu.mimuw.irs.rcp.controller.editor.workflow.part;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.RootEditPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.eclipse.ui.views.properties.TextPropertyDescriptor;

import pl.edu.mimuw.irs.core.model.design.net.Fragmentation;
import pl.edu.mimuw.irs.core.model.design.net.IRSArc;
import pl.edu.mimuw.irs.core.model.design.net.IRSNode;
import pl.edu.mimuw.irs.core.model.petrinet.Arc;
import pl.edu.mimuw.irs.core.model.petrinet.Node;
import pl.edu.mimuw.irs.core.util.Position;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.policy.NodeComponentEditPolicy;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.policy.WorkflowXYLayoutEditPolicy;
import pl.edu.mimuw.irs.rcp.util.MessageCode;
import pl.edu.mimuw.irs.rcp.util.UITexts;
import pl.edu.mimuw.irs.rcp.view.figure.FigureWrapper;

/**
 * @author Jakub Rauch
 * Created on: 2009-05-16
 */
public abstract class IRSAbstractNodeEditPart extends AbstractNodeEditPart implements IRSNodeEditPart, PropertyChangeListener {
	
	// *** Attributes

	protected NodePropertySource propertySource;
	private boolean highlighted;
	
	public NodePropertySource getPropertySource() {
		if (propertySource == null) {
			propertySource = new NodePropertySource(getIRSNode());
		}
		return propertySource;
	}
	public boolean isHighlighted() {
		return highlighted;
	}
	public void setHighlighted(boolean highlighted) {
		this.highlighted = highlighted;
		refreshVisuals();
	}
	
	// *** Methods

	@Override
	protected IFigure createFigure() {
		IRSNode node = (IRSNode)getModel();
		return getNodeShape().getShape(node.getPosition());
	}
	
	@Override
	public void activate() {
		super.activate();
		getNode().addPropertyChangeListener(this);
	}
	@Override
	public void deactivate() {
		super.deactivate();
		getNode().removePropertyChangeListener(this);
	}

	protected void refreshVisuals() {
		super.refreshVisuals();
		
		FigureWrapper wrapper = (FigureWrapper)getFigure();
		IRSNode node = (IRSNode)getModel();
		
		Label label = new Label(node.getId());
		if (!node.getFragmentation().equals(Fragmentation.NONE)) {
			label.setText(label.getText() + "\n" + node.getFragmentation().toString());
		}
		if (node.getChildren().size() > 0) {
			wrapper.setLineWidth(2);
			label.setText(label.getText() + "\n[" + node.getChildren().size() + "]");
		} else {
			wrapper.setLineWidth(1);
		}
		Font placeFont;
		//if (isHighlighted()) placeFont = new Font(null, "Arial", 8, SWT.BOLD);
		placeFont = new Font(null, "Arial", 8, SWT.NORMAL);

		label.setFont(placeFont);
		label.setTextAlignment(PositionConstants.CENTER);
        label.setTextPlacement(PositionConstants.SOUTH);
		label.setBounds(wrapper.getBounds().crop(IFigure.NO_INSETS));
		wrapper.setLabel(label);
	}
	
	public IRSNode getIRSNode() {
		return (IRSNode)getModel();
	}
	
	@Override
	protected List<Arc> getModelSourceConnections() {
		Set<Arc> allArcs = getIRSNode().getOutgoingArcs();
		List<Arc> visibleArcs = new LinkedList<Arc>();
		for (Arc arc : allArcs) {
			if (!isExpanded((IRSNode)arc.getSource()) &&
					isExpanded(((IRSNode)arc.getSource()).getParent()) &&
					!isExpanded((IRSNode)arc.getTarget())&&
					isExpanded(((IRSNode)arc.getTarget()).getParent()))
				visibleArcs.add((IRSArc)arc);
		}
		
		return visibleArcs;
	}
	
	private boolean isExpanded(IRSNode node) {
		if (node == null) return false;
		if (node.isRoot() && node.isExpanded()) return true;
		else if (node.isExpanded() && isExpanded(node.getParent())) return true;
		else return false;
	}
	
	@Override
	protected List<IRSArc> getModelTargetConnections() {
		Set<Arc> allArcs = getIRSNode().getIncomingArcs();
		List<IRSArc> visibleArcs = new LinkedList<IRSArc>();
		for (Arc arc : allArcs) {
//			if (!((IRSNodeInterface)arc.getSource()).isExpanded() &&
//					((IRSNodeInterface)arc.getSource()).getParent().isExpanded() &&
//					!((IRSNodeInterface)arc.getTarget()).isExpanded() &&
//					((IRSNodeInterface)arc.getTarget()).getParent().isExpanded())
//				visibleArcs.add((IRSArc)arc);
			if (!isExpanded((IRSNode)arc.getSource()) &&
					isExpanded(((IRSNode)arc.getSource()).getParent()) &&
					!isExpanded((IRSNode)arc.getTarget())&&
					isExpanded(((IRSNode)arc.getTarget()).getParent()))
				visibleArcs.add((IRSArc)arc);
		}
		
		return visibleArcs;
	}
	
	@Override
	public void moveTo(Rectangle newBounds) {
		getIRSNode().setPosition(new Position(newBounds.x, newBounds.y));
	}
	
	@Override
	protected void createEditPolicies() {
		installEditPolicy(EditPolicy.LAYOUT_ROLE, new WorkflowXYLayoutEditPolicy());
		installEditPolicy(EditPolicy.COMPONENT_ROLE, new NodeComponentEditPolicy());
	}
	
	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		if (Node.POSITION.equals(evt.getPropertyName())) {
			if (!(evt.getNewValue() instanceof Position)) return; 
			Position position = (Position) evt.getNewValue();
			Rectangle bounds = getFigure().getBounds();
			Rectangle newBounds = new Rectangle(position.getX(), position.getY(),
					bounds.width, bounds.height);
			getFigure().setBounds(newBounds);
		}
		refresh(getRoot());
	}
	
	@SuppressWarnings("unchecked")
	private void refresh(EditPart editPart) {
		this.highlighted = false;
		editPart.refresh();
		List<EditPart> children = editPart.getChildren();
		if (editPart instanceof RootEditPart) {
			for (EditPart childEditPart : children) {
				refresh(childEditPart);
			}
		} else if (editPart instanceof IRSNodeEditPart) {
			IRSNodeEditPart nodeEditPart = (IRSNodeEditPart)editPart;
			IRSNode node = nodeEditPart.getIRSNode();
			if (node.isExpanded()) {
				for (EditPart childEditPart : children) {
					refresh(childEditPart);
				}
			}
		} else {
			for (EditPart childEditPart : children) {
				childEditPart.refresh();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Object getAdapter(Class key) {
		if (key == IPropertySource.class)
			return getPropertySource();
		return super.getAdapter(key);
	}
	
	// *** Inner classes
	
	public class NodePropertySource implements IPropertySource {

		protected static final String PROPERTY_ID = "properties.node.id";
		
		// *** Attributes
		
		private IRSNode node;
		public IRSNode getNode() {
			return node;
		}
		
		private IPropertyDescriptor[] propertyDescriptors;
		
		private String orgId;
		
		// *** Constructors
		
		public NodePropertySource(IRSNode node) {
			this.node = node;
			this.orgId = node.getId();
		}
		
		// *** Methods

		@Override
		public Object getEditableValue() {
			// TODO Auto-generated method stub
			return null;
		}
		
		@Override
		public IPropertyDescriptor[] getPropertyDescriptors() {
			if (propertyDescriptors == null) {
	            // Create a descriptors and set a category
	            PropertyDescriptor idDescriptor = 
	            	new TextPropertyDescriptor(PROPERTY_ID, UITexts.get(MessageCode.PROPERTIES_FIELD_ID));
	            idDescriptor.setCategory(UITexts.get(MessageCode.PROPERTIES_CATEGORY_GENERAL));
	            
	            propertyDescriptors = new IPropertyDescriptor[] { 
	            		idDescriptor };
            }
            return propertyDescriptors;
		}

		@Override
		public Object getPropertyValue(Object id) {
			if (PROPERTY_ID.equals(id)) {
				return node.getId();
			}
			return null;
		}

		@Override
		public boolean isPropertySet(Object id) {
			if (PROPERTY_ID.equals(id)) {
	        	String curValue = (String)getPropertyValue(id);
				return !curValue.equals(orgId);
			}
			return false;
		}

		@Override
		public void resetPropertyValue(Object id) {
			if (PROPERTY_ID.equals(id)) {
				setPropertyValue(id, this.orgId);
			}
		}

		@Override
		public void setPropertyValue(Object id, Object value) {
			if (PROPERTY_ID.equals(id)) {
				node.setId((String)value); // XXX : catch
			}
		}
	}
}
